home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-05-30 | 12.2 KB | 404 lines | [TEXT/ttxt] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Grayscale Image
- *
- * Write an image into a file in the TIFF format
- *
- * The present TIFF writer creates a Class G TIFF file (for gray-scale
- * images), See Appendix G of the TIFF specification.
- *
- * The program writes the following required tags
- * NewSubfileType
- * ImageWidth
- * ImageLength
- * RowsPerStrip (To make strips around 8K)
- * StripOffsets
- * StripByteCounts
- * XResolution (72 dpi)
- * YResolution (72 dpi)
- * ResolutionUnit
- *
- * In addition, the class G TIFF files are required to have the following
- * tags with the following values
- * SamplesPerPixel = 1
- * BitsPerSample = 4 or 8
- * Compression = 1 or 5(LZW)
- * PhotometricInterpretation = 0 or 1
- *
- * In addition, if the image has name, it's written under
- * ImageDescription,
- * The title, if specified, is written as
- * DocumentName
- * tag
- *
- * $Id: write_tiff.cc,v 2.0 1995/03/06 21:55:06 oleg Exp oleg $
- *
- ************************************************************************
- */
-
- #include "image.h"
- #include "endian_io.h"
- #pragma implementation "tiff.h"
- #include "tiff.h"
-
- // Write a TIFF header into the file
- void TIFFHeader::write(EndianOut& file)
- {
- if( magic == TIFF_BIGENDIAN )
- file.set_bigendian();
- else if( magic == TIFF_LITTLEENDIAN )
- file.set_littlendian();
- else
- _error("Unknown magic word %x",magic);
- file.write_short(magic);
- file.write_short(version);
- file.write_long(diroffset);
- }
-
-
- /*
- *------------------------------------------------------------------------
- * TIFF Directory
- * Note that all items in the TIFF directory are threaded in an ascending
- * order of their tags. So, when a new DirectoryItem is created, it inserts
- * itself into the thread at proper position.
- * Note, if some directory item has count field equal 0, the item is
- * considered "dummy" and it's not written to the TIFF directory in the file.
- *
- */
-
- class TIFFDirectoryItem : public TIFFDirEntry
- {
- static int no_entries; // So all TIFFDirEntry's got threaded
- static TIFFDirectoryItem * entries[250]; // through in an ascending order
-
- protected:
- virtual void write(EndianOut& file) = 0; // Write this field into a file
- virtual void write_value(EndianOut& file) = 0;// Write additional data
-
- public:
- TIFFDirectoryItem(const short _tag, const DataType _type, const long _count,
- const long _value);
- void write_all(EndianOut& file); // Write the entire TIFF directory
- };
-
-
- int TIFFDirectoryItem::no_entries = 0;
- TIFFDirectoryItem * TIFFDirectoryItem::entries[250];
-
- // Insert an item into the directory
- // in an ascending order of its tag
- TIFFDirectoryItem::TIFFDirectoryItem
- (const short _tag, const DataType _type, const long _count,
- const long _value)
- : TIFFDirEntry(_tag,_type,_count,_value)
- {
- if( no_entries >= sizeof(entries)/sizeof(entries[0]) )
- _error("Can't add a new entry, the directory is full, %d items",
- no_entries);
- register int pos; // Find a position where the present
- for(pos=0; pos<no_entries; pos++) // item belongs (according to its tag)
- if( entries[pos]->tag > tag )
- break;
- register int i; // Move items with bigger tags toward
- for(i=no_entries-1; i >= pos; i--) // the end of the array to make space
- entries[i+1] = entries[i]; // for our item
- entries[pos] = this;
- no_entries++;
- }
-
- // Write the entire directory and clean up
- void TIFFDirectoryItem::write_all(EndianOut& file)
- {
- int no_nondummy_items = 0;
- register int i;
- for(i=0; i<no_entries; i++) // Count non-dummy dir entries
- if( entries[i]->count != 0 )
- no_nondummy_items++;
- assert( no_nondummy_items > 0 );
-
- file.write_short(no_nondummy_items);
- for(i=0; i<no_entries; i++)
- if( entries[i]->count != 0 )
- entries[i]->write(file);
- file.write_long(0); // Specify this is the end of the IFD
- // no other IFD is to follow
- for(i=0; i<no_entries; i++) // Handle items that store their values
- if( entries[i]->count != 0 ) // after the directory
- entries[i]->write_value(file);
- no_entries = 0;
- }
-
-
- /*
- *------------------------------------------------------------------------
- * Particular types of DirectoryItems
- */
-
- // Scalar item. It keeps its value in the
- // val_offset field of the DirItem
- // Note, values shorter than long are
- // LEFT-justified
- class ScalarTIFFDE : public TIFFDirectoryItem
- {
- protected:
- void write(EndianOut& file); // Write this field into a file
- void write_value(EndianOut& file) {} // Value is a part of the item, and
- // is written along with the item
-
- public:
- ScalarTIFFDE(const short _tag, const TSHORT _value)
- : TIFFDirectoryItem(_tag,SHORT,1,(unsigned long)_value << 16) {}
- ScalarTIFFDE(const short _tag, const TLONG _value)
- : TIFFDirectoryItem(_tag,LONG,1,_value) {}
- };
-
- // Write this item into a file
- void ScalarTIFFDE::write(EndianOut& file)
- {
- file.write_short(tag);
- file.write_short(type);
- file.write_long(count);
- file.write_long(val_offset);
- }
-
-
- // String item, the string itself is written
- // after the IFD, the val_offset field of
- // the item stores an offset to the string
- // characters.
- class StringTIFFDE : public TIFFDirectoryItem
- {
- protected:
- void write(EndianOut& file); // Write this field into a file
- void write_value(EndianOut& file); // Write the body of the string
- // (after the directory is written)
-
- streampos offset_for_val; // for the val_offset field
-
- public:
- StringTIFFDE(const short _tag, const char * str)
- : TIFFDirectoryItem(_tag,ASCII, str == 0 || str[0] == '\0' ? //empty string
- 0 : strlen(str)+1,(long)str) {}
- };
-
- // Write this item into a file
- void StringTIFFDE::write(EndianOut& file)
- {
- assert( count > 0 );
- file.write_short(tag);
- file.write_short(type);
- file.write_long(count);
- offset_for_val = file.tellp();
- file.write_long(0); // write a dummy for now
- }
-
- // Write the body of the string,
- // Note, a padding may be necessary to
- // make the string end at a word boundary
- void StringTIFFDE::write_value(EndianOut& file)
- {
- streamoff pos = file.tellp(); // where are we now
- // Write a body of the string, '\0'
- // and (possible) an extra byte
- file.write((char *)val_offset,(strlen((char *)val_offset)+1+1) & ~1);
- // to keep word-boundary align
- streampos pos_new = file.tellp();
- assert( file.seekp(offset_for_val).good() );
- file.write_long(pos); // Write the offset within the dir elem
- assert( file.seekp(pos_new).good() ); // Restore the file position
- // to continue writing
- }
-
-
- // Directory item that refers to an array
- // (that can contain strip offsets, strip
- // byte counts, or other info specific to
- // each strip)
- class ArrayTIFFDE : public TIFFDirectoryItem
- {
- protected:
- virtual void write(EndianOut& file); // Write this field into a file
- virtual void write_value(EndianOut& file); // Write the body of the array
- // (after the directory is written)
-
- streampos offset_for_val; // for the val_offset field
- long * array;
-
- public:
- ArrayTIFFDE(const short _tag, const long no_elems);
- virtual ~ArrayTIFFDE(void);
- long& operator [] (const int index) // Get hold of an element of the array
- { assert( index >= 0 && index < count ); return array[index]; }
- };
-
- // Constructor - allocate the array
- ArrayTIFFDE::ArrayTIFFDE(const short _tag, const long no_elems)
- : TIFFDirectoryItem(_tag, LONG, no_elems, 0)
- {
- assert( count > 0 );
- array = new long[count];
- }
-
-
- // Dispose of the array
- ArrayTIFFDE::~ArrayTIFFDE(void)
- {
- assert( array != 0 );
- delete [] array;
- array = 0;
- }
-
-
- // Write this item into a file
- void ArrayTIFFDE::write(EndianOut& file)
- {
- assert( count > 0 );
- file.write_short(tag);
- file.write_short(type);
- file.write_long(count);
- offset_for_val = file.tellp();
- file.write_long(0); // write a dummy for now
- }
-
- // Write array elements (if more than 1)
- // after the directory
- void ArrayTIFFDE::write_value(EndianOut& file)
- {
- long int pos; // Where the array is to be written
- register int i;
- if( count == 1 ) // If there is only one elem, keep
- pos = array[0]; // the offset with the dir elem itself
- else
- {
- pos = file.tellp(); // Write the array separately
- for(i=0; i<count; i++)
- file.write_long(array[i]);
- }
- streampos pos_new = file.tellp();
- assert( offset_for_val != 0 && file.seekp(offset_for_val).good() );
- file.write_long(pos); // Write the offset within the dir elem
- assert( file.seekp(pos_new).good() ); // Restore the file position
- // to continue writing
- }
-
- // Rational number: an array of two LONGs
- class RationalTIFFDE : public ArrayTIFFDE
- {
- virtual void write(EndianOut& file); // Write this field into a file
- public:
- RationalTIFFDE(const short _tag, const int numerator,const int denominator)
- : ArrayTIFFDE(_tag,2)
- { array[0] = numerator; array[1] = denominator; type = RATIONAL; }
- };
-
- // Write this item into a file: only one
- // modification: a rational has count 1, though
- // it's an array of 2 longs
- void RationalTIFFDE::write(EndianOut& file)
- {
- assert( count == 2 );
- count = 1; // temporarily: that's what we write
- ArrayTIFFDE::write(file); // into the tag
- count = 2;
- }
-
-
- // Strip item, that refers to an array
- // that contains offsets to the strips (set
- // of rows) of the image
- // No compression is used at present
- class StripTIFFDE : public ArrayTIFFDE
- {
- protected:
- void write_value(EndianOut& file); // Write the array of strip ptrs
- // and strips themselves
- // (after the directory is written)
-
- ArrayTIFFDE strip_byte_counts; // It is not necessary unless compress-
- // ion is used; but neverthelesss
- short rows_per_strip;
- const IMAGE& image;
-
- public:
- StripTIFFDE(const short _no_strips, const short _rows_per_strip,
- const IMAGE& _image)
- : ArrayTIFFDE(TIFFTAG_STRIPOFFSETS,_no_strips),
- strip_byte_counts(TIFFTAG_STRIPBYTECOUNTS,_no_strips),
- rows_per_strip(_rows_per_strip), image(_image) {}
- };
-
- // Write the array of strip ptrs
- // and strips themselves
- // (after the directory is written)
- // Note the trick! StripByteCount
- // has a bigger tag, i.e. strip_byte_
- // count will be written AFTER the
- // strips are written, i.e., when
- // strip sizes are already known
- void StripTIFFDE::write_value(EndianOut& file)
- {
- assert( count > 0 );
- register int strip;
- for(strip=0; strip<count; strip++)
- {
- array[strip] = file.tellp(); // where are we now, beg of a strip
- register int i,j; // Writing a strip
- for(i=strip*rows_per_strip;
- i<(strip+1)*rows_per_strip && i < image.q_nrows(); i++)
- for(j=0; j<image.q_ncols(); j++)
- file.write_byte(image(i,j));
- strip_byte_counts[strip] = file.tellp() - array[strip];
- }
- ArrayTIFFDE::write_value(file);
- }
-
- /*
- *------------------------------------------------------------------------
- * Root module
- */
-
- void IMAGE::write_tiff(const char * file_name,const char * title) const
- {
- is_valid();
-
- message("\nPreparing a TIFF file with name '%s'\n",file_name);
-
- EndianOut file(file_name);
-
- TIFFHeader header;
- header.write(file);
-
- ScalarTIFFDE subfile_tag(TIFFTAG_SUBFILETYPE,(unsigned)0);
- ScalarTIFFDE width_tag(TIFFTAG_IMAGEWIDTH,(unsigned)q_ncols());
- ScalarTIFFDE length_tag(TIFFTAG_IMAGELENGTH,(unsigned)q_nrows());
- ScalarTIFFDE compr_tag(TIFFTAG_COMPRESSION,(unsigned short)COMPRESSION_NONE);
- ScalarTIFFDE phot_tag(TIFFTAG_PHOTOMETRIC,
- (unsigned short)PHOTOMETRIC_MINISBLACK);
- ScalarTIFFDE resunit_tag(TIFFTAG_RESOLUTIONUNIT,
- (unsigned short)RESUNIT_INCH);
- // Means 72 pixels per unit (inch)
- RationalTIFFDE xres_tag(TIFFTAG_XRESOLUTION,72,1);
- RationalTIFFDE yres_tag(TIFFTAG_YRESOLUTION,72,1);
-
- ScalarTIFFDE samplesize_tag(TIFFTAG_SAMPLESPERPIXEL,(unsigned short)1);
- assert( bits_per_pixel == 8 );
- ScalarTIFFDE depth_tag(TIFFTAG_BITSPERSAMPLE,(unsigned short)8);
-
-
- StringTIFFDE desc_tag(TIFFTAG_IMAGEDESCRIPTION, name );
- StringTIFFDE docname_tag(TIFFTAG_DOCUMENTNAME, title );
-
- const int strip_target_size = 8*1024;
- unsigned int rows_per_strip = strip_target_size / q_ncols();
- int no_strips = (q_nrows() + rows_per_strip - 1)/rows_per_strip;
- ScalarTIFFDE striprs_tag(TIFFTAG_ROWSPERSTRIP,rows_per_strip);
- StripTIFFDE stripoffs_tag(no_strips, rows_per_strip, *this);
-
- subfile_tag.write_all(file);
- file.close();
- }
-
-